﻿using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace ProjectTW.Foliage
{
    public sealed class FoliageDB : ScriptableObject
    {
        private static string s_ConfigName = "FoliageDB";
        private static FoliageDB s_Instance;

        public static FoliageDB Instance
        {
            get
            {
                if (s_Instance == null)
                {
                    s_Instance = Resources.Load<FoliageDB>(s_ConfigName);
                }

                return s_Instance;
            }
        }

        [SerializeField]
        public List<FoliagePrototype> m_Prototypes = new List<FoliagePrototype>();
        public FoliageWind GlobalFoliageWind = new FoliageWind();
        
        private static Dictionary<int, FoliagePrototype> s_PrototypesDictionary;
        
        public static List<FoliagePrototype> UnSortedPrototypes
        {
            get
            {
                return Instance.m_Prototypes;
            }
        }

        public static Dictionary<int, FoliagePrototype> sortedPrototypes
        {
            get
            {
                if (s_PrototypesDictionary == null)
                {
                    s_PrototypesDictionary = new Dictionary<int, FoliagePrototype>();

                    for (int i = 0; i < Instance.m_Prototypes.Count; i++)
                    {
                        s_PrototypesDictionary.Add(Instance.m_Prototypes[i].Id, Instance.m_Prototypes[i]);
                    }
                }

                return s_PrototypesDictionary;
            }
            private set
            {
                s_PrototypesDictionary = value;
            }
        }

        private byte GenPrototypeId
        {
            get
            {
                const byte max = byte.MaxValue;

                for (byte i = 1; i < max; i++)
                {
                    if (sortedPrototypes.ContainsKey(i))
                    {
                        continue;
                    }
                    return i;
                }
                throw new IndexOutOfRangeException("Can't create any more prototypes! Limit Reached!");
            }
        }

        /// <summary>
        /// 创建一个新的原型对象
        /// </summary>
        public FoliagePrototype AddPrototype(Texture2D texture, GameObject prefab, float minWidth, float minHeight, float maxWidth, float maxHeight, float spread, int layer,
                                             Color healthyColor, Color dryColor)
        {
            byte id = GenPrototypeId;
            FoliagePrototype prototype = FoliagePrototype.CreatePrototype(texture, prefab, minWidth, minHeight, maxWidth, maxHeight, spread, layer, id, healthyColor, dryColor);
            FoliageMeshManager.GenerateFoliageMeshInstances(id);
            FoliageMeshManager.RegenerateQueueInstances();
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                UnityEditor.EditorUtility.SetDirty(this);
            }
#endif
            return prototype;
        }

        public FoliagePrototype AddPrototype(DetailPrototype detailPrototype)
        {
            return AddPrototype(detailPrototype.prototypeTexture, detailPrototype.prototype, detailPrototype.minWidth, detailPrototype.minHeight, detailPrototype.maxWidth,
                    detailPrototype.maxHeight, detailPrototype.noiseSpread * 10, 0, detailPrototype.healthyColor, detailPrototype.dryColor
            );
        }

        public FoliagePrototype AddPrototype(Texture2D texture)
        {
            return AddPrototype(texture, null, 1.5f, 1.5f, 2f, 2f, 1f, 0, FoliagePrototype.DefaultHealthyColor, FoliagePrototype.DefaultDryColor);
        }

        public FoliagePrototype AddPrototype(GameObject prefab)
        {
            if (prefab == null)
            {
                return null;
            }

            Color healthyColor = FoliagePrototype.DefaultHealthyColor;
            Color dryColor = FoliagePrototype.DefaultDryColor;
            MeshRenderer mRenderer = prefab.GetComponentInChildren<MeshRenderer>();

            if (mRenderer != null)
            {
                Material mat = mRenderer.sharedMaterial;

                if (mat != null)
                {
                    if (mat.HasProperty("_HealthyColor"))
                    {
                        healthyColor = mat.GetColor("_HealthyColor");
                    }

                    if (mat.HasProperty("_DryColor"))
                    {
                        dryColor = mat.GetColor("_DryColor");
                    }
                }
            }

            return AddPrototype(null, prefab, 1.5f, 1f, 2f, 1.2f, 1f, prefab == null ? 0 : prefab.layer, healthyColor, dryColor);
        }

        public void RemovePrototype(FoliagePrototype prototype)
        {
            m_Prototypes.Remove(prototype);
            sortedPrototypes.Remove(prototype.Id);
            FoliageMeshManager.DestroyMeshInstance(prototype.Id);
            FoliageMeshManager.RegenerateQueueInstances();
#if UNITY_EDITOR
            prototype.DisposeOfCurrentMaterial();
#endif
            GC.SuppressFinalize(prototype);
        }

        /// <param name="prototype"></param>
        internal void RegisterNewPrototype(FoliagePrototype prototype)
        {
            m_Prototypes.Add(prototype);

            if (!sortedPrototypes.ContainsKey(prototype.Id))
            {
                sortedPrototypes.Add(prototype.Id, prototype);
            }
        }

        public void UpdateShaderWindSettings()
        {
            FoliagePrototype prototype;

            for (int i = 0; i < UnSortedPrototypes.Count; i++)
            {
                prototype = UnSortedPrototypes[i];

                if (!prototype.Enabled)
                {
                    continue;
                }

                prototype.ApplyWind();
            }
        }

        public void UpdateShaderGeneralSettings()
        {
            for (int i = 0; i < UnSortedPrototypes.Count; i++)
            {
                UnSortedPrototypes[i].UpdateManagerInformation();
            }
        }

        public static void SwitchPrototypes(FoliagePrototype from, FoliagePrototype to)
        {
            int fromIndex = UnSortedPrototypes.IndexOf(from);
            int toIndex = UnSortedPrototypes.IndexOf(to);

            if (fromIndex == -1 || toIndex == -1)
            {
                return;
            }

            // replace slots
            UnSortedPrototypes[fromIndex] = to;
            UnSortedPrototypes[toIndex] = from;
            sortedPrototypes = null; // reset organization.
            var fromID = from.Id;
            var toID = to.Id;

            // replace ids
            from.m_FoliageIdInternal = toID;
            to.m_FoliageIdInternal = fromID;
            from.GenerateInstantiatedMesh(from.HealthyColor, from.DryColor);
            to.GenerateInstantiatedMesh(to.HealthyColor, to.DryColor);

            if (FoliageManager.Instance != null)
            {
                FoliageMeshManager.RegenerateQueueInstances();
                FoliageMeshManager.GenerateFoliageMeshInstances();
                Instance.UpdateShaderGeneralSettings();
            }

            Debug.Log("Succesfully switched prototype " + from.Name + " with prototype " + to.Name);
        }
    }
}